﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//预定义符号也是在预处理期间处理的
//__FILE__ //进⾏编译的源⽂件
//__LINE__ //⽂件当前的⾏号
//__DATE__ //⽂件被编译的⽇期
//__TIME__ //⽂件被编译的时间
//__STDC__ //如果编译器遵循ANSI C，其值为1，否则未定义

//VS2022并没有严格的遵循ANSI C这个标准
//int main()
//{
//
//	printf("%s\n", __FILE__);
//	printf("%d\n", __LINE__);
//	printf("%s\n", __DATE__);
//	printf("%s\n", __TIME__);
//	//printf("%s\n", __STDC__);
//
//	return 0;
//}



//#define 定义常量
//#define name stuff
//#define M 100
//#define STR "hehe"
//#define MAX 1000
//#define reg register //为 register这个关键字，创建⼀个简短的名字
//#define do_forever for(;;) //⽤更形象的符号来替换⼀种实现
//#define CASE break;case //在写case语句的时候⾃动把 break写上。
//// 如果定义的 stuff过⻓，可以分成⼏⾏写，除了最后⼀⾏外，每⾏的后⾯都加⼀个反斜杠(续⾏符)。
////续行符之后空格都不能加
//#define DEBUG_PRINT printf("file:%s\tline:%d\t \
// date:%s\ttime:%s\n" ,\
// __FILE__,__LINE__ , \
// __DATE__,__TIME__ )

//
//#define 后面一般不加；可能会导致错误

//#define 机制包括了⼀个规定，允许把参数替换到⽂本中，这种实现通常称为宏（macro）或定义宏
//（define macro）。
//#define name( parament-list ) stuff
//              参数表
//参数列表的左括号必须与name紧邻，如果两者之间有任何空⽩存在，参数列表就会被解释为stuff的一部分

//#define SQUARE(N) (N)*(N)   //宏
////宏的参数是不计算直接替换进去的
////宏的参数可能是表达式，表达式中会有操作符，宏的内容中也会有操作符，等宏的参数替换到文本中
////可能为因为操作符的优先级，导致计算的结果有偏差，这里建议使用(),来保证运算的顺序
//int main()
//{
//	int x = 5;
//
//	//int ret = SQUARE((x + 1));//36
//	int ret = SQUARE(x+1);//11
//	//int ret = x + 1 * x + 1;//结果就为11
//	printf("%d\n", ret);
//
//	return 0;
//}

//#define DOUBLE(N) ((N) + (N))
//int main()
//{
//	int x = 6;
//
//	int ret = 10 * DOUBLE(x);
//
//	printf("%d\n", ret);
//
//	return 0;
//}



//带有副作⽤的宏参数
//当宏参数在宏的定义中出现超过⼀次的时候，如果参数带有副作⽤，那么你在使⽤这个宏的时候就可
//能出现危险，导致不可预测的后果。副作⽤就是表达式求值的时候出现的永久性效果。
//#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
//int main()
//{
//	int a = 7;
//	int b = 5;
//	int m = MAX(a, b);
//	printf("%d\n", m);
//
//	return 0;
//}
//int main()
//{
//	int a = 7;
//	int b = 5;
//	int m = MAX(a++, b++);
//	printf("%d\n", m);//8
//	printf("%d\n", a);//9
//	printf("%d\n", b);//6
//	return 0;
//}
//int main()
//{
//	int a = 10;
//	int b = a + 1;
//	int b = ++a;//这个代码就是有副作用，把a的值改掉了
//	return 0;
//}

//int Max(int x, int y)
//{
//	return x > y ? x : y;
//}
//int main()
//{
//	int a = 7;
//	int b = 5;
//	int m = Max(a++, b++);
//	printf("%d\n", m);//7
//	printf("%d\n", a);//8
//	printf("%d\n", b);//6
//	return 0;
//}



//宏替换的规则
//在程序中扩展#define定义符号和宏时，需要涉及⼏个步骤。
//1. 在调⽤宏时，⾸先对参数进⾏检查，看看是否包含任何由#define定义的符号。如果是，它们⾸先
//被替换。
//2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏，参数名被他们的值所替换。
//3. 最后，再次对结果⽂件进⾏扫描，看看它是否包含任何由#define定义的符号。如果是，就重复上
//述处理过程。
//注意：
//1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏，不能出现递归。
//2. 当预处理器搜索#define定义的符号的时候，字符串常量的内容并不被搜索。




//宏和函数的对⽐
//宏通常被应⽤于执⾏简单的运算。
//⽐如在两个数找出较⼤的⼀个时，写成中下⾯的宏，更有优势⼀些。
//不使用函数原因
//1. ⽤于调⽤函数和从函数返回的代码可能⽐实际执⾏这个⼩型计算⼯作所需要的时间更多。所以宏⽐
//函数在程序的规模和速度⽅⾯更胜⼀筹。
//2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使⽤。反之
//这个宏怎可以适⽤于整形、⻓整型、浮点型等可以⽤于 > 来⽐较的类型。宏的参数是类型⽆关
//的。

//
//和函数相⽐宏的劣势：
//1. 每次使⽤宏的时候，⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短，否则可能⼤幅度增加程序
//的⻓度。
//2. 宏是没法调试的。
//3. 宏由于类型⽆关，也就不够严谨。
//4. 宏可能会带来运算符优先级的问题，导致程容易出现错。
//#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
//int main()
//{
//	int a = 3;
//	int b = 5;
//	int m = MAX(a, b);
//	printf("%d\n", m);
//
//	return 0;
//}
//
//#define MALLOC(N,TYPE) (TYPE*)malloc(N*sizeof(TYPE)) 
//int main()
//{
//	int* p = (int*)malloc(10 * sizeof(int));
//
//	int* p1 = MALLOC(10, int);
//	//int* p1 = (int*)malloc(10*sizeof(int));
//
//	return 0;
//}


//1. 建议如果执行简单的运算，可以使用宏
//2. 如果功能复杂，直接使用函数

//在c++中引入内联函数，inline具备了函数的优势又具备了宏的优势







//
//#和##
//#运算符
//#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。
//#运算符所执⾏的操作可以理解为”字符串化“。

//int main()
//{
//	printf("helloworld\n");
//	printf("hello""world\n");//这两种结果一样
//
//	return 0;
//}

//
//#define PRINT(v,formet) printf("the value of "#v" is "formet"\n", v)
//int main()
//{
//	int a = 3;
//	//the value of a is 3
//	//printf("the value of a is %d\n", a);
//	PRINT(a, "%d");
//
//	int b = 20;
//	//the value of b is 20
//	//printf("the value of b is %d\n", b);
//	PRINT(b, "%d");
//
//	float f = 3.14;
//	//printf("the value of f is %f\n", f);
//	PRINT(f, "%f");
//
//
//	return 0;
//}



//## 运算符
// ## 可以把位于它两边的符号合成⼀个符号，它允许宏定义从分离的⽂本⽚段创建标识符。
//  ## 被称为记号粘合
//这样的连接必须产⽣⼀个合法的标识符。否则其结果就是未定义的

//#define CAT(X,Y) X##Y
//int main()
//{
//	int Class115 = 100;
//	printf("%d\n",CAT(Class, 115));
//
//	return 0;
//}

//麻烦
//int int_max(int x, int y)
//{
//	return x > y ? x : y;
//}
//float float_max(float x, float y)
//{
//	return x > y?x:y;
//}


//宏定义
//#define GENERIC_MAX(type) \
//type type##_max(type x, type y)\
//{\
//    return (x>y?x:y); \
//}
//GENERIC_MAX(int)
//GENERIC_MAX(float)
//int main()
//{
//    int  m = int_max(3, 4);
//
//    printf("%d\n", m);
//
//    return 0;
//}



//命令行定义
//许多C 的编译器提供了⼀种能⼒，允许在命令⾏中定义符号。⽤于启动编译过程。


//条件编译
//在编译⼀个程序的时候我们如果要将⼀条语句（⼀组语句）编译或者放弃是很⽅便的。
// 因为我们有条件编译指令。



//#define __DEBUG__
//int main()
//{
//	int i = 0;
//	int arr[10] = { 0 };
//	for (i = 0; i < 10; i++)
//	{
//		arr[i] = i + 1;
//#ifdef __DEBUG__
//		printf("%d ", arr[i]);//为了观察数组是否赋值成功。 
//#endif //__DEBUG__
//	}
//	return 0;
//}

//
//#if 常量表达式
// //...
//#endif
////常量表达式由预处理器求值。
//
//如：
//#define __DEBUG__ 1
//#if __DEBUG__
////..
//#endif
////2.多个分⽀的条件编译
//#if 常量表达式
////...
//#elif 常量表达式
////...
//#else
////...
//#endif
//3.判断是否被定义
//#if defined(symbol)
//#ifdef symbol
//#if !defined(symbol)
//#ifndef symbol
//4.嵌套指令
// #if defined(OS_UNIX)
// #ifdef OPTION1
// unix_version_option1();
// #endif
// #ifdef OPTION2
// unix_version_option2();
// #endif
// #elif defined(OS_MSDOS)
// #ifdef OPTION2
// msdos_version_option2();
// #endif
//#endif


//#define M 0
//int main()
//{
//	//这里就是判断M是否被定义过，关于值是多少不关心
//	
//
//	//这两种表示的意思一样
//#if defined(M)
//	printf("hehe\n");
//#endif
//
//#ifdef M
//	printf("hehe\n");
//#endif
//
//	return 0;
//}



//头⽂件中的 ifndef / define / endif是⼲什么⽤的 ?.
//是为了防止头文件的多次重复包含